/*
 * EasyCamera
 *
 * Copyright (c) 2020.  Frezrik frezrik@126.com
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package com.camera;

import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.os.Handler;
import android.os.SystemClock;

import com.camera.callback.ErrorCallback;
import com.camera.callback.PreviewCallback;
import com.camera.uvchelper.AbstractUVCCameraHandler;
import com.camera.uvchelper.UVCCameraHelper;
import com.camera.widget.YuvSurfaceView;
import com.frezrik.common.thread.SingleThread;
import com.frezrik.common.usb.UsbCamera;

/**
 * Description: UvcCamera API
 * Author: zhouming
 * CreateDate: 2020/6/2
 */
public class EasyUvcCamera {
    private final String TAG = getClass().getSimpleName();

    private Context mContext;
    private UVCCameraHelper mUVCCameraHelper;
    private SingleThread mCameraThread;
    //private YuvSurfaceView mYuvSurfaceView;
    private PreviewCallback mPreviewCallback;
    private ErrorCallback mCallback;
    private boolean mHasRequestPermission;
    private int mVendorId;
    private int mProductId;
    //private byte[] mData;

    public void initUvcCamera(Context context, int vendorId, int productId) {
        Logger.d(TAG, "initUvcCamera:");
        if (context == null) {
            throw new IllegalStateException("context is null");
        }
        mContext = context.getApplicationContext();
        mVendorId = vendorId;
        mProductId = productId;

        if (mUVCCameraHelper == null) {
            mUVCCameraHelper = new UVCCameraHelper();
        }
    }

    public boolean isUvcCameraAttach() {
        return UsbCamera.isDeviceAttach(mContext, mVendorId, mProductId);
    }

    /**
     *
     * @param format UVCCamera.FRAME_FORMAT_YUYV(0) or UVCCamera.FRAME_FORMAT_MJPEG(1)
     */
    public void setFrameStreamFormat(int format) {
        if (mUVCCameraHelper != null) {
            mUVCCameraHelper.setDefaultFrameFormat(format);
        }
    }

    public void setPreviewResolution(int width, int height) {
        if (mUVCCameraHelper != null) {
            mUVCCameraHelper.setDefaultPreviewSize(width, height);
        }
    }

    public void openCamera() {
        getExecuteHandler().post(new Runnable() {
            @Override
            public void run() {
                if (!isOpenCamera()) {
                    if (mUVCCameraHelper != null && mContext != null) {
                        Logger.d(TAG, "openCamera:");
                        mUVCCameraHelper.initUSBMonitor(mContext, mUsbDevlistener);
                        //mUVCCameraHelper.setOnPreviewFrameListener(mOnPreViewResultListener);
                        mUVCCameraHelper.registerUSB();
                    } else {
                        if (mCallback != null) {
                            mCallback.OnError(ErrorCallback.CAMERA_OPEN_FAIL);
                        }
                        Logger.e(TAG, "openCamera: null");
                    }

                } else {
                    Logger.d(TAG, "openCamera: has open");
                }
            }
        });
    }

    public boolean isOpenCamera() {
        return mUVCCameraHelper != null && mUVCCameraHelper.isCameraOpened();
    }

    public void closeCamera() {
        //mYuvSurfaceView = null;
        //mData = null;
        getExecuteHandler().post(new Runnable() {
            public void run() {
                if (mUVCCameraHelper != null) {
                    Logger.d(TAG, "closeCamera:");
                    mUVCCameraHelper.setOnPreviewFrameListener(null);
                    mUVCCameraHelper.stopPreview();
                    mUVCCameraHelper.closeCamera();
                    mUVCCameraHelper.unregisterUSB();
                    mUVCCameraHelper.release();
                    mUVCCameraHelper = null;
                }

                mHasRequestPermission = false;
            }
        });
    }

    public void startStream() {
        getExecuteHandler().post(() -> {
            if (mUVCCameraHelper != null) {
                mUVCCameraHelper.setOnPreviewFrameListener(mOnPreViewResultListener);

                long start = System.currentTimeMillis();
                while (!isOpenCamera()) {
                    SystemClock.sleep(100);
                    if (System.currentTimeMillis() - start > 1500) {
                        Logger.d(TAG, "startPreview: camera not open");
                        if (mCallback != null) {
                            mCallback.OnError(ErrorCallback.CAMERA_NOT_OPEN);
                        }
                        break;
                    }
                }

                Logger.d(TAG, "startPreview:");
                mUVCCameraHelper.startPreview();
            }
        });
    }

    public void stopStream() {
        getExecuteHandler().post(() -> {
            //mYuvSurfaceView = null;
            //mData = null;
            /*if (Utils.isNeedPowerOff()) {
                Logger.d(TAG, "Uvc closeCamera");
                closeCamera();
            } else {*/
            if (mUVCCameraHelper != null) {
                Logger.d(TAG, "stopStream:");
                mUVCCameraHelper.setOnPreviewFrameListener(null);
                if (isOpenCamera()) {
                    Logger.d(TAG, "stopStream: stopPreview");
                    mUVCCameraHelper.stopPreview();
                }
            }
            //}
        });

    }

    /*public byte[] updateFrame() {
        if (!isOpenCamera())
            return null;
        else
            return mData;
    }*/

    /*public void setPreview(YuvSurfaceView surface) {
        mYuvSurfaceView = surface;
    }*/

    public void setPreviewCallback(PreviewCallback callback) {
        mPreviewCallback = callback;
    }

    public void setErrorCallback(ErrorCallback callback){
        mCallback = callback;
    }

    private AbstractUVCCameraHandler.OnPreviewFrameListener mOnPreViewResultListener =
            (byte[] data) -> {
                //Logger.d(TAG, "OnPreviewFrameListener:" + data.length);
                /*mData = data;
                if (mYuvSurfaceView != null) {
                    mYuvSurfaceView.setNV21Data(data);
                }*/
                if (mPreviewCallback != null) {
                    mPreviewCallback.onPreview(data);
                }
            };


    private UVCCameraHelper.OnUsbDevConnectListener mUsbDevlistener =
            new UVCCameraHelper.OnUsbDevConnectListener() {
                public void onAttachDev(UsbDevice device) {
                    if (mUVCCameraHelper != null && mUVCCameraHelper.getUsbDeviceCount() != 0) {
                        if (device.getVendorId() == mVendorId && device.getProductId() == mProductId && !mHasRequestPermission && mUVCCameraHelper != null) {
                            Logger.d(TAG, "onAttachDev:" + device.getProductName());
                            mUVCCameraHelper.requestPermission(mVendorId, mProductId);
                            mHasRequestPermission = true;
                        }

                    }
                }

                public void onDetachDev(UsbDevice device) {
                    if (device.getVendorId() == mVendorId && device.getProductId() == mProductId && mHasRequestPermission) {
                        Logger.d(TAG, "onDetachDev:" + device.getProductName());
                        closeCamera();
                    }

                }

                public void onConnectDev(UsbDevice device, boolean isConnected) {
                    if (device.getVendorId() == mVendorId && device.getProductId() == mProductId) {
                        Logger.d(TAG, "onConnectDev:" + device.getProductName() + "," +
                                "mHasRequestPermission:" + mHasRequestPermission);
                        if (mHasRequestPermission) {
                            mUVCCameraHelper.startPreview();
                        }
                    }
                }

                public void onDisConnectDev(UsbDevice device) {
                    if (device.getVendorId() == mVendorId && device.getProductId() == mProductId) {
                        Logger.d(TAG, "onDisConnectDev:" + device.getProductName());
                        closeCamera();
                    }
                }
            };

    private Handler getExecuteHandler() {

        if (mCameraThread == null) {
            mCameraThread = new SingleThread(this.getClass().getSimpleName());
        }
        mCameraThread.start();
        return mCameraThread.mThreadHandler;
    }

}
